#include "plat_api.h"
#include <mtd/mtd-user.h>

#include <unistd.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <fcntl.h> 
#include <time.h> 
#include <sys/ioctl.h> 
#include <sys/mount.h> 
//#include <linux/compiler.h> 
#include <mtd/mtd-user.h> 
#include <string.h> 


/*********************************************/
#define dbg(...) 
//#define dbg printf
#define ADT_FLASH_VERSION 1
#define ADT_FLASH_MAGIC   (('A'<<16)||('D'<<8)||('T'))

const char *g_devicename = "/dev/mtd7";
const char *g_partionname = "data";
const char *g_devicename_bak = "/dev/mtd8";
const char *g_partionname_bak = "bak";
/*********************************************/

#define _KBYTES (1024U)
#define FLASH_ERASES_SIZE (64*_KBYTES)
#define FLASH_DATA_SIZE (64*_KBYTES)
#define MIN(x,y) (((x)<(y))? (x) :(y))
#define MAX(x,y) (((x)>(y))? (x) :(y))

struct s_adt_flash {
    unsigned int magic;
    unsigned short version;
    unsigned short crc;
    unsigned short len;
    unsigned short res;
    unsigned int res1[5];
};


#define ADT_FLASH_HEAD_SIZE sizeof(struct s_adt_flash)

void hex_dump(const unsigned char *buf, unsigned short len) {
    unsigned int i;
    for(i = 0; i < len; i++) {
        printf("%02X ", buf[i]);
    }
    printf("\n");
}

static unsigned short  _crc16_query_table(unsigned char *buffer, unsigned short  length) 
{	 
    
    static unsigned short const g_wCRC16Table[256] = {      
        0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,     
        0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,     
        0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,      
        0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,     
        0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,       
        0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,     
        0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,     
        0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,     
        0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,     
        0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,        
        0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,     
        0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,     
        0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,     
        0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,     
        0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,        
        0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,     
        0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,     
        0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,     
        0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,     
        0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,        
        0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,     
        0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,     
        0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,     
        0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,     
        0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,       
        0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,     
        0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,     
        0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,     
        0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,     
        0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,       
        0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,     
        0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
    };  
	unsigned short wResult = 0;	  
	unsigned short wTableNo = 0;	   
	unsigned short  i;
	for( i = 0; i < length; i++) 	
	{	  
		wTableNo = ((wResult & 0xff) ^ (buffer[i] & 0xff));	 
		wResult = ((wResult >> 8) & 0xff) ^ g_wCRC16Table[wTableNo];	   
	}	  
	//dbg(" _crc16_query_table:%x\n",wResult);
	return wResult; 	
}	

int adt_open_config_file(char *p_arg, unsigned short argBufLen, char*p_type)
{
	FILE * fp; 
	int res; 
	if ((fp = popen(p_arg, p_type) ) == NULL) 
	{ 
		perror("popen");
		printf("LINE:%d popen error: \n",__LINE__); 
		return -1; 
	} 
	else
	{
		 while(fgets(p_arg, argBufLen, fp)) 
		{ 
			//printf("%s", p_arg); 
		}
		if ( (res = pclose(fp))!= 0) 
		{ 
			printf("close popen file pointer fp error :%d!\n",res); 
			return res;
		} 
	}
	return 0;
}

static int32_t flash_partion_init(const char* devicename, const char* partion_name)
{
#define _ARG_LEN 256
#define _NAME_BUF_LEN 64
	char  namebuf[_NAME_BUF_LEN]={0};
	char arg[_ARG_LEN]={0}; 
    unsigned short len;
	len = snprintf(arg, _ARG_LEN, "cat /proc/mtd | busybox grep  %s | awk '{print $1}' | sed 's/://'", partion_name);
	if(0 != adt_open_config_file(arg, _ARG_LEN, "r")) {
		return -1;
	}	
	if (0 == arg[0]) {
		printf("err: adt flash device no exist!\n"); 
		return -1;		
	}
    len = strlen(arg);
    if(arg[len-1] == '\n') {
        arg[len-1] = 0;
    }
	sprintf(namebuf, "/dev/%s", arg);
    if(0 != strcmp(namebuf, devicename)) {
        printf("err: adt flash devicename don't match (%s) (%s)\n", namebuf, devicename);
        return -1;
    }
    return 0;
}

static int32_t _flash_write_NoErase(const char *devicename, unsigned char *buf, unsigned int len, unsigned int off) {
    int fd_d, wlen;
    fd_d = open(devicename, O_SYNC | O_RDWR);
    if(fd_d < 0) {
        printf("err: adt flash open %s failed\n", devicename);
        return -1;
    }
    if(off !=0 ) {
        lseek(fd_d, off, SEEK_SET);
    }
    wlen = write(fd_d, buf, len);
    close(fd_d);
    return wlen;
}

static int32_t _flash_write(const char *devicename, unsigned char *buf, unsigned int len) {
    int fd_d;
    int32_t wlen;
    unsigned int i, erase_count;
    struct mtd_info_user mtd;
    struct erase_info_user e;
    len = MIN(len, FLASH_DATA_SIZE);
    fd_d = open(devicename, O_SYNC | O_RDWR);
    if(fd_d < 0) {
        printf("err: adt flash open %s failed\n", devicename);
        return -1;
    }
    if (ioctl(fd_d, MEMGETINFO, &mtd) < 0) {
        printf("err: adt flash %s is not a MTD flash device", devicename);
        goto err;
    }
    if (len > mtd.size || mtd.erasesize != FLASH_ERASES_SIZE) {
        printf("err: adt flash info not match\n");
        goto err;
    }
    
    erase_count = (unsigned int)(len + mtd.erasesize - 1) / mtd.erasesize;
    dbg("adt flash erase_count is %d\n", erase_count);
    
    e.length = mtd.erasesize;
    e.start = 0;
    
    for (i = 1; i <= erase_count; i++) {
        if (ioctl(fd_d, MEMERASE, &e) < 0) {
            printf("err: adt flash erase error at 0x%x on %s", e.start, devicename);
            goto err;
        }
        e.start += mtd.erasesize;
    }
    
    wlen = write(fd_d, buf, len);
    if(wlen < 0) {
        printf("err: adt flash write faild %d\n", len);
        goto err;
    }else if((unsigned int)wlen != len) {
        printf("wrong: adt flash write len not match %d/%d\n", wlen, len);
    }
    close(fd_d);
    return 0;
err:
    close(fd_d);
    return -1;
    
}


int32_t _flash_read(const char* devicename, unsigned char *buf, unsigned int len, unsigned int off) {
    int rlen = 0, fd;
    fd= open(devicename, O_RDWR);
    dbg("%s fd = %d\n", devicename, fd);
    if(fd) {
        len = MIN(len, FLASH_DATA_SIZE);
        if(off !=0 ) {
            lseek(fd, off, SEEK_SET);
        }
        rlen = read(fd, buf, len);
        if((unsigned int)rlen != len) {
            printf("err: adt flash %s read len = %d/%d!\n", devicename, rlen, len); 
        }
        close(fd);
    }else {
        printf("err: adt flash %s open err\n", devicename);
    }
    return rlen;
}


int32_t _adt_flash_read(const char *devicename, unsigned char *buf, unsigned int len) {
    int32_t rlen = 0;
    unsigned char head_buf[ADT_FLASH_HEAD_SIZE];
    unsigned char head_len = ADT_FLASH_HEAD_SIZE;
    struct s_adt_flash *head = (struct s_adt_flash*)head_buf;
    if(head_len == _flash_read(devicename, head_buf, head_len, 0)) {
        if(len >= head->len && \
            head->magic == ADT_FLASH_MAGIC && \
            head->version <= ADT_FLASH_VERSION ) {
            /*1.magic 相等且向下兼容*/
            rlen = _flash_read(devicename, buf, len, ADT_FLASH_HEAD_SIZE);
            if(rlen > 0){
                unsigned short cal_crc = _crc16_query_table(buf, len);
                if(cal_crc != head->crc) {
                    printf("adt flash %s crc err %x/%x\n", devicename, cal_crc, head->crc);
                    rlen = 0;
                }
            }
            /*
            2.向下兼容
            */
        } 
    }
    return rlen;
}

int32_t _adt_flash_write(const char *devicename, unsigned char *buf, unsigned int len) {
    int32_t rlen = 0;
    unsigned char head_buf[ADT_FLASH_HEAD_SIZE];
    unsigned char head_len = ADT_FLASH_HEAD_SIZE;
    struct s_adt_flash *head = (struct s_adt_flash*)head_buf;
    if(len < FLASH_DATA_SIZE - ADT_FLASH_HEAD_SIZE) {
        /*pack flash head*/
        memset(head_buf, 0, sizeof(head_buf));
        head->len = len;
        head->crc = _crc16_query_table(buf, len);
        head->version = ADT_FLASH_VERSION;
        head->magic = ADT_FLASH_MAGIC;
        /*write flash body and head*/
        if(_flash_write(devicename, head_buf, head_len) < 0) {
            return -1;
        }
        rlen = _flash_write_NoErase(devicename, buf, len, ADT_FLASH_HEAD_SIZE);
    }
    return rlen;
}


void adt_flash_init(void) {
    if(flash_partion_init(g_devicename, g_partionname) < 0 || 
        flash_partion_init(g_devicename_bak, g_partionname_bak) < 0) {
        printf("exit: adt flash init faild\n");
        exit(-1);
    };
}

int32_t adt_flash_read(unsigned char *buf, unsigned int len) {
    int32_t rlen, rlen_bak;
    rlen = _adt_flash_read(g_devicename, buf, len);
    rlen_bak = _adt_flash_read(g_devicename_bak, buf, len);
    if(rlen <= 0 && rlen_bak > 0) {
        rlen = rlen_bak;
        printf("adt flash write bak to data\n");
        _adt_flash_write(g_devicename, buf, rlen);
    }else if(rlen > 0 && rlen_bak <= 0) {
        printf("adt flash write data to bak\n");
        _adt_flash_write(g_devicename_bak, buf, rlen);
    }
    return rlen;

}
int32_t adt_flash_write(unsigned char *buf, unsigned int len) {
    int32_t wlen = 0;
    printf("adt flash start write\n");
    wlen = _adt_flash_write(g_devicename, buf, len);
    if(_adt_flash_read(g_devicename, buf, len) <= 0) {
        wlen = 0;
        printf("adt flash write data verify faild\n");
    }
    _adt_flash_write(g_devicename_bak, buf, len);
    if(_adt_flash_read(g_devicename_bak, buf, len) <= 0) {
        wlen = 0;
        printf("adt flash write bak verify faild\n");
    }
    printf("adt flash write down\n");
    return wlen;
}

void adt_flash_write_nocheck(unsigned char *buf, unsigned int len) {
    _adt_flash_write(g_devicename, buf, len);
    _adt_flash_write(g_devicename_bak, buf, len);
}

/*
const char *text = "123456789\n";
int main(void ) {
    int len;
    unsigned char buf[FLASH_DATA_SIZE];
    adt_flash_init();
    memset(buf, 0, strlen(text));
    len = adt_flash_read(buf, strlen(text));
    printf("read flash len is %d\n", len);
    if(len > 0) {
        buf[len] = 0;
        printf("%s\n", buf);
    }
    adt_flash_write(buf, strlen(text));
    return 0;
}
*/


